﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

using NUnit.Framework ;

using ElfDoc;
using DocumentModelFake;

namespace ElfDocExtensionMethodsUnitTest
{

    [TestFixture]
    class DynamicDictionaryUnitTest
    {

        [Test]
        public void demo_dynamic_dictionary_should_be_pokable_with_property_and_property_values()
        {

            //-Arrange

            // creating a dynamic dictionary.
            dynamic person = new DynamicDictionary();

            //-Act

            // set dynamic properties ; TrySetMember method is called.
            person.FirstName = "Ellen";
            person.LastName = "Adams";

            //-Assert

            // poke 2 properties with 2 values
            Assert.That(person.DynamicObjectCount == 2);
            Assert.That(person.firstname == "Ellen");
            Assert.That(person.lastname == "Adams");

            //-Act

            // poke 2 existing values and 1 new property 
            person.FirstName = "Johny";
            person.LastName = "Carson";
            person.Title = "Mr";

            //-Assert
            Assert.That(person.DynamicObjectCount == 3);
            Assert.That(person.firstname == "Johny");
            Assert.That(person.lastname == "Carson");
            Assert.That(person.title == "Mr");

            //-Act

            // poke dotted context on the model
            Dictionary<string, int> dottedContext = new Dictionary<string, int>();
                dottedContext.Add("CaseConductor.CaseNote", 1);
                dottedContext.Add("CaseConductor.Case", 2);
                dottedContext.Add("CaseConductor.Person", 3);

            // poke 1 more property, which is a dictionary<string,int>
            person.DotContext = dottedContext;

            //-Assert
            Assert.That(person.DotContext["CaseConductor.CaseNote"] == 1);
            Assert.That(person.DotContext["CaseConductor.Case"] == 2);
            Assert.That(person.DotContext["CaseConductor.Person"] == 3);


        }

        [Test]
        public void dynamic_model_Given_a_dotted_context_poked_to_a_model_When_dotted_context_key_used_in_ValueByKeyTuple_call_Then_Should_yield_same_value_as_indexed_property()
        {

            //-Arrange

            dynamic model = TestableModel.GetModelTestData();

            // get property values in usual way ; by standard property access and usual key value method
            var actualPropertyValue = model.CaseConductor.CaseNoteList[1].NoteText;
            var expected = model.ValueByKeyTuple("CaseConductor.CaseNoteList[1].NoteText");

            // get property values by usual dotted context key value
            Dictionary<string, int> dottedContext = new Dictionary<string, int>();
            dottedContext.Add("CaseConductor.CaseNoteList", 1);

            //-Act

            // poke dotted context
            model.DottedContext = dottedContext;

            // get value by dotted context
            var actual = model.ValueByKeyTuple("CaseConductor.CaseNoteList[.].NoteText");

            //-Assert

            // the expected value by the usual key method should be the same as by the dotted context
            Assert.AreEqual(expected.ObjectValue, actual.ObjectValue);

        }

    }

    [TestFixture]
    class ObjectByKeyValueKeyMapperStringExtension
    {

        [Test]
        public void extension_method_ObjectByKeyValueKeyMapperValueByKeyTuple_should_return_expected_key_CaseConductorCaseNoteList1()
        {

            //-Arrange

            var dottedContext = new Dictionary<string, int>();
            dottedContext.Add("CaseConductor.CaseNoteList", 1);
            string key = "CaseConductor.CaseNoteList[.]";
            var expected = "CaseConductor.CaseNoteList[1]";

            //-Act

            var actual = key.ObjectByKeyValueKeyMapper( dottedContext ) ;

            //-Assert
            Assert.AreEqual(expected , actual ) ;

        }

        [Test]
        public void extension_method_ObjectByKeyValueKeyMapperValueByKeyTuple_with_edge_cases_should_return_expected_key_as_sent_in()
        {

            //-Arrange

            var dottedContext = new Dictionary<string, int>();
            dottedContext.Add("CaseConductor.CaseNoteList", 1);

            //-Act

            //-Assert

            // some edge cases
            Assert.AreEqual( "", "".ObjectByKeyValueKeyMapper(null) );
            Assert.AreEqual( "", "".ObjectByKeyValueKeyMapper(dottedContext) );

            Assert.AreEqual( "ABC", "ABC".ObjectByKeyValueKeyMapper(null));
            Assert.AreEqual("ABC", "ABC".ObjectByKeyValueKeyMapper(dottedContext));

            Assert.AreEqual("CaseConductor.CaseNoteList[1]", "CaseConductor.CaseNoteList[1]".ObjectByKeyValueKeyMapper(null));
            Assert.AreEqual("CaseConductor.CaseNoteList[1]", "CaseConductor.CaseNoteList[1]".ObjectByKeyValueKeyMapper(dottedContext));

            string key = Guid.NewGuid().ToString();
            Assert.AreEqual(key, key.ObjectByKeyValueKeyMapper(null));
            Assert.AreEqual(key, key.ObjectByKeyValueKeyMapper(dottedContext));

        }

    }

    [TestFixture]
    class ValueByKeyTupleExtension
    {

        [Test]
        public void extension_method_ValueByKeyTuple_given_public_function_on_multiSut_object_then_Age_invoke_should_yield_same_value_as_when_called_in_usual_way()
        {
            //-Arrange

            var dtNow = DateTime.Now;
            var multiFake = new MultiFake { DOB = dtNow };

            //-Act

            var actual = multiFake.ValueByKeyTuple("Age()");

            //-Assert


            // should be zero
            Assert.AreEqual("0", actual.StringValue);
            Assert.AreEqual(0, (int)actual.ObjectValue);

        }

        [Test]
        public void extension_method_ValueByKeyTuple_given_DateTime_var_then_ToShortDateString_invoke_should_yield_same_value_as_when_called_in_usual_way()
        {
            //-Arrange 

            var dtNow = DateTime.Now;

            var multiFake = new MultiFake { DOB = dtNow };

            //-Act

            var actual = multiFake.ValueByKeyTuple("DOB.ToShortDateString()");

            //-Assert

            // should short date string done in the normal way
            Assert.AreEqual(actual.StringValue, dtNow.ToShortDateString());

        }

        [Test]
        public void extension_method_ValueByKeyTuple_given_MultiFake_var_then_invoke_of_non_existant_function_should_yield_value_not_obtained_and_not_except()
        {
            //-Arrange 

            var dtNow = DateTime.Now;

            var multiFake = new MultiFake { DOB = dtNow };

            //-Act

            // we should get a null here, and not except
            var actual = multiFake.ValueByKeyTuple("ThisFunctionIsNotAFunctionOfTheObject()");

            //-Assert

            // the code should get here and not except
            Assert.That(1 == 1);
            Assert.That(actual.ValueObtained == false );

        }

        [Test]
        public void extension_method_ValueByKeyTuple_given_MultiFake_var_then_invoke_of_function_that_raises_exception_should_yield_value_not_obtained()
        {
            //-Arrange 

            var dtNow = DateTime.Now;

            var multiFake = new MultiFake {};

            //-Act

            // should except here
            bool isExceptionCausedByNormalUseOfFunction = false  ; 
            try
            {
                multiFake.FooCausesExceptionOnInside();
            }
            catch(Exception ex)
            {
                isExceptionCausedByNormalUseOfFunction = true; 
            }

            // we should get a null here, and not except
            var actual  = multiFake.ValueByKeyTuple("FooCausesExceptionOnInside()");

            //-Assert

            // the code should get here and not except
            Assert.IsTrue(isExceptionCausedByNormalUseOfFunction);
            Assert.That( actual.ValueObtained == false ) ;

        }

        [Test]
        public void datetime_property_should_yield_expected_subproperties()
        {

            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act
            var expected = DateTime.Now ;
            model.CaseConductor.Person[0].DOB = expected;

            var tday = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Day");
            var tDayOfWeek = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.DayOfWeek");
            var t6 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.DayOfYear");
            var t7 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Hour");
            var t8 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.DayOfYear");
            var t9 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Hour");
            var ta = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Kind");
            var tb = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Millisecond");
            var tc = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Minute");
            var tmonth = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Month");
            var tf = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Second");
            var tg = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Ticks");
            var th = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.TimeOfDay");
            var tyear = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.Year");

            // these are yielding null because have set up ValueByKeyTuple to handel function calls but just properties
            var tx8 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.ToShortDateString()");
            var tx9 = model.CaseConductor.Person[0].ValueByKeyTuple("DOB.ToShortDateString()");

            //-Assert
            Assert.AreEqual(expected.Day.ToString(), tday.StringValue);
            Assert.AreEqual(expected.Month.ToString(), tmonth.StringValue);
            Assert.AreEqual(expected.Year.ToString(), tyear.StringValue);
            Assert.AreEqual(expected.DayOfWeek.ToString(), tDayOfWeek.StringValue);

        }

        [Test]
        public void string_property_should_yield_expected_upto_depth_3()
        {

            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act
            var expected = "Smith";
            model.CaseConductor.Person[0].Surname = expected;

            var t1 = model.CaseConductor.Person[0].ValueByKeyTuple("Surname");
            var t2 = model.CaseConductor.ValueByKeyTuple("Person[0].Surname");
            var t3 = model.ValueByKeyTuple("CaseConductor.Person[0].Surname");


            //-Assert
            Assert.AreEqual(expected + "", t1.StringValue);
            Assert.AreEqual(expected + "", t2.StringValue);
            Assert.AreEqual(expected + "", t2.StringValue);

        }

        [Test]
        public void non_existant_string_property_should_yield_null_upto_depth_3()
        {

            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act
            var expected = "Smith";
            model.CaseConductor.Person[0].Surname = expected;

            var t1 = model.CaseConductor.Person[0].ValueByKeyTuple("CaseSpellingMistake");
            var t2 = model.CaseConductor.Person[0].ValueByKeyTuple("Case.Person.firstname");
            var t3 = model.CaseConductor.ValueByKeyTuple("Person[0].fUrsTNameSpeelingTypo");
            var t4 = model.ValueByKeyTuple("Case.Person[0].fUrsTNameSpeelingTypo");

            //-Assert
            Assert.IsNull(t1.ObjectValue );
            Assert.IsNull(t2.ObjectValue);
            Assert.IsNull(t3.ObjectValue);
            Assert.IsNull(t4.ObjectValue);

        }

    }

    [TestFixture]
    class ValueByKeyExtension
    {

        [Test]
        public void blank_index_in_key_should_yield_null()
        {

            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act

            object actual1 = model.ValueByKey("CaseConductor.CaseNoteListList[].NoteDt");

            //-Assert
            Assert.IsNull(actual1);
        }

        [Test]
        public void caseNoteList_should_be_iterable_with_poperty_access()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act

            IList list = (IList)model.ValueByKey("CaseConductor.CaseNoteList");

            //-Assert

            // iterate over a lst of items and get properties 
            foreach (var item in list)
            {
                var noteDt = item.ValueByKey("NoteDt");
                var noteID = item.ValueByKey("NoteID");
                var noteText = item.ValueByKey("NoteText");
                if (noteDt != null)
                {
                    Assert.IsNotNull(noteDt);
                }
                if (noteDt != null)
                {
                    Assert.IsNotNull(noteID);
                }
                if (noteDt != null)
                {
                    Assert.IsNotNull(noteText);
                }
            }

        }

        [Test]
        public void datetime_property_should_yield_expected_upto_depth_3()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act
            var expected = DateTime.Now;

            model.CaseConductor.Person[0].DOB = expected;

            object actual1 = model.CaseConductor.Person[0].ValueByKey("DOB");
            object actual2 = model.CaseConductor.ValueByKey("Person[0].DOB");
            object actual3 = model.ValueByKey("CaseConductor.Person[0].DOB");

            //-Assert

            Assert.AreEqual(expected, actual1);
            Assert.AreEqual(expected, actual2);
            Assert.AreEqual(expected, actual3);

        }

        [Test]
        public void string_property_should_yield_expected_upto_depth_3()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act
            var expected = "Smith";
            model.CaseConductor.Person[0].Surname = expected;

            object actual1 = model.CaseConductor.Person[0].ValueByKey("Surname");
            object actual2 = model.CaseConductor.ValueByKey("Person[0].Surname");
            object actual3 = model.ValueByKey("CaseConductor.Person[0].Surname");

            //-Assert
            Assert.AreEqual(expected +"" , actual1 ) ;
            Assert.AreEqual(expected + "", actual2 ) ;
            Assert.AreEqual(expected + "", actual3 ) ;

        }

        [Test]
        public void non_existant_property_key_should_return_null()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act

            object actual = model.ValueByKey("TheObjectIsNotThere");

            //-Assert
            Assert.IsNull( actual , "a non existent property should return null and not except" ) ; 

        }

        [Test]
        public void property_by_index_should_yield_note()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act

            //
            var actualNote0 = model.CaseConductor.CaseNoteList[0];
            var actualNote1 = model.CaseConductor.CaseNoteList[1];

            //
            object expectedNote0 = (Note)model.ValueByKey("CaseConductor.CaseNoteList[0]");
            object expectedNote1 = (Note)model.ValueByKey("CaseConductor.CaseNoteList[1]");

            //-Assert
            Assert.AreEqual( expectedNote0 , actualNote0 ) ;
            Assert.AreEqual( expectedNote1 , actualNote1 ) ;

        }

        [Test]
        public void property_by_index_of_property_should_yield_string()
        {
            //-Arrange

            // test model
            var model = TestableModel.GetModelTestData();

            //-Act

            var actualNoteText0 = model.CaseConductor.CaseNoteList[0].NoteText;
            var actualNoteText1 = model.CaseConductor.CaseNoteList[1].NoteText;

            object expectedNoteText0 = model.ValueByKey("CaseConductor.CaseNoteList[0].NoteText");
            object expectedNoteText1 = model.ValueByKey("CaseConductor.CaseNoteList[1].NoteText");

            //-Assert
            Assert.AreEqual( expectedNoteText0 , actualNoteText0 ) ;
            Assert.AreEqual( expectedNoteText1 , actualNoteText1 ) ;

        }

    }

    [TestFixture]
    class ObjectDictionaryExtension
    {

        [Test]
        public void PropertyObjectDictionary_of_null_should_be_return_dictionary_of_len_0()
        {
            //-Arrange
            //-Act
            object person = null ;
            var d = person.PropertyObjectDictionary();
            //-Assert
            Assert.AreEqual(0 , d.Count());
        }

        [Test]
        [ExpectedException(typeof(System.Collections.Generic.KeyNotFoundException))]
        public void property_access_of_non_existant_key_should_except()
        {
            //-Arrange
            //-Act
            object person = null;
            var d = person.PropertyObjectDictionary();
            var surname = d["Surname"];
        }

        [Test]
        public void ObjectDictionary_property_should_be_return_string_value()
        {
            //-Arrange
            //-Act
            var person = new Person { Surname = Guid.NewGuid().ToString() };
            var d = person.PropertyObjectDictionary();
            var surname = d["Surname"];
            //-Assert
            Assert.AreEqual("System.String", surname.GetType().FullName );
        }

        [Test]
        public void string_property_should_be_retrievable()
        {
            //-Arrange
            //-Act
            var person = new Person { Surname = Guid.NewGuid().ToString() };
            var d = person.PropertyObjectDictionary();
            var surname = d["Surname"];
            //-Assert
            Assert.AreEqual(person.Surname , surname );
        }

        [Test]
        public void nth_object_property_should_be_retrievable()
        {
            //-Arrange
            var cc = new CaseConductor();

            cc.Person = new List<Person>();
            cc.Person.Add(new Person { Surname = Guid.NewGuid().ToString() });

            cc.CaseNoteList = new List<Note>();
            cc.CaseNoteList.Add(new Note { NoteText = "A1" });
            cc.CaseNoteList.Add(new Note { NoteText = "B2" });

            //-Act

            // get the object dictionary
            var d = cc.PropertyObjectDictionary();

            // we should be able to get an IList of note
            IList caseNoteList = (IList)d["CaseNoteList"];

            //
            Note note0 = (Note)d.GetObjectFromObjectDictionary("CaseNoteList[0]");
            Note note1 = (Note)d.GetObjectFromObjectDictionary("CaseNoteList[1]");

            //-Assert
            Assert.AreEqual(cc.CaseNoteList[0].NoteText, note0.NoteText );
            Assert.AreEqual(cc.CaseNoteList[1].NoteText, note1.NoteText);

        
        }

        [Test]
        public void date_property_should_be_retrievable()
        {
            //-Arrange
            //-Act
            var expected = DateTime.Now;
            var person = new Person { DOB = expected };
            var d = person.PropertyObjectDictionary();
            var actual = d["DOB"];
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void bool_property_should_be_retrievable()
        {
            //-Arrange
            //-Act
            var expected = true ;
            var person = new Person { IsAdUser = expected };
            var d = person.PropertyObjectDictionary();
            var actual = d["IsAdUser"];
            //-Assert
            Assert.AreEqual( expected, actual);
        }

        [Test]
        public void enum_property_should_be_retrievable()
        {
            //-Arrange
            //-Act
            var expected = GenderEnum.Male ;
            var person = new Person { GenderEnum = expected };
            var d = person.PropertyObjectDictionary();
            var actual = d["GenderEnum"];
            //-Assert
            Assert.AreEqual(expected, actual);
        }

    }

    [TestFixture]
    class BetweenStringExtension
    {

        [Test]
        public void number_between_index_tail_should_be_0()
        {
            //-Arrange
            string expected = "0"; 
            //-Act
            string actual = "a.b.c[0].d.f".Between("[", "]");
            //-Assert
            Assert.AreEqual( expected, actual);
        }

        [Test]
        public void on_empty_string_between_should_be_emptystring()
        {
            //-Arrange
            string expected = "";
            //-Act
            string actual = "".Between("", "");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_empty_start_string_between_should_be_emptystring()
        {
            //-Arrange
            string expected = "";
            //-Act
            string actual = "abc".Between("", "]");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_empty_end_string_between_should_be_emptystring()
        {
            //-Arrange
            string expected = "";
            //-Act
            string actual = "abc".Between("[", "");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_string_with_no_brackets_should_be_emptystring()
        {
            //-Arrange
            string expected = "";
            //-Act
            string actual = "abc".Between("[", "]");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_string_with_brackets__but_nothing_between_should_be_emptystring()
        {
            //-Arrange
            string expected = "";
            //-Act
            string actual = "abc[]xyz".Between("[", "]");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_string_with_brackets_and_guid_between_should_be_guid()
        {
            //-Arrange
            string expected = Guid.NewGuid().ToString() ;
            string target = String.Format("abc[{0}]xyz", expected); 
            //-Act
            string actual = target.Between("[", "]");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void on_string_with_2_brackets_with_guid_between_should_be_yield_first_guid()
        {
            //-Arrange
            string expected = Guid.NewGuid().ToString();
            string expected2 = Guid.NewGuid().ToString();
            string target = String.Format("abc[{0}]xyzabc[{1}]xyz", expected, expected2 );
            //-Act
            string actual = target.Between("[", "]");
            //-Assert
            Assert.AreEqual(expected, actual);
        }

    }

    [TestFixture]
    class TailStringExtension
    {

        [Test]
        public void stringempty_tail_should_yield_stringempty()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("", "".Tail());
        }

        [Test]
        public void a_tail_should_yield_stringempty()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("", "a".Tail());
        }

        [Test]
        public void ab_tail_should_yield_b()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("b", "a.b".Tail());
        }

        [Test]
        public void abc_tail_should_yield_ba()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("b.c", "a.b.c".Tail());
        }

        [Test]
        public void abcd_tail_should_yield_expected_bcd()
        {
            //-Arrange
            string expected = "b.c.d";
            //-Act
            var actual = ("a.b.c.d" ).Tail();
            //-Assert
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void any_number_of_dotted_parts_tail_should_yield_expected()
        {
            //-Arrange
            string head= "HEAD";
            string expected = "";
            string dottedString = string.Empty;
            //-Act
            int dottedPartsNum = 5;
            dottedString += head ;
            for (int i = 0; i < dottedPartsNum; i++)
            {
                string newPart = "." + Guid.NewGuid() ;
                newPart = "a";
                expected += (i == 0) ? newPart : "." + newPart;
                dottedString += "." + newPart ;
            }
            var actual = dottedString.Tail();
            //-Assert
            Assert.AreEqual(expected, actual);
        }

    }

    [TestFixture]
    class HeadStringExtension
    {

        [Test]
        public void stringempty_head_should_yield_stringempty()
        { 
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("", "".Head());
        }

        [Test]
        public void a_head_should_yield_a()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("a", "a".Head());
        }

        [Test]
        public void ba_head_should_yield_b()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("b", "b.a".Head());
        }

        [Test]
        public void cba_head_should_yield_c()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("c", "c.b.a".Head());
        }

        [Test]
        public void guidcba_head_should_yield_expected_random_string()
        {
            //-Arrange
            string expected = Guid.NewGuid().ToString() ; 
            //-Act
            var actual = expected + ".c.b.a".Head(); 
            //-Assert
            Assert.AreEqual( expected , actual );
        }
        
        [Test]
        public void any_number_of_dotted_parts_head_should_yield_expected()
        {
            //-Arrange
            string expected = "EXPECTED";
            string dottedString = string.Empty;
            //-Act
            int dottedPartsNum = 5;
            dottedString += expected ; 
            for (int i = 0; i < dottedPartsNum ; i++)
            {
                dottedString += "." + Guid.NewGuid() ; 
            }
            var actual = dottedString.Head();
            //-Assert
            Assert.AreEqual(expected, actual);
        }
    
    }

    [TestFixture]
    class TailLastStringExtension
    {
        
        [Test]
        public void stringempty_should_yield_stringempty()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("", "".TailLast());
        }
        
        [Test]
        public void a_should_yield_a()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual( "a" , "a".TailLast() ) ;
        }

        [Test]
        public void ba_should_yield_b()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("b", "a.b".TailLast());
        }
        [Test]
        public void abc_should_yield_bc()
        {
            //-Arrange
            //-Act
            //-Assert
            Assert.AreEqual("c", "a.b.c".TailLast());
        }
        [Test]
        public void abcguid_should_yield_expected_random_string()
        {
            //-Arrange
            string expected = Guid.NewGuid().ToString();
            //-Act
            var actual = ("a.b.c."+expected ).TailLast();
            //-Assert
            Assert.AreEqual(expected, actual);
        }
        [Test]
        public void any_number_of_dotted_parts_head_should_yield_expected()
        {
            //-Arrange
            string expected = "EXPECTED";
            string dottedString = string.Empty;
            //-Act
            int dottedPartsNum = 5;
            for (int i = 0; i < dottedPartsNum; i++)
            {
                dottedString += "." + Guid.NewGuid();
            }
            dottedString += "." + expected;
            var actual = dottedString.TailLast();
            //-Assert
            Assert.AreEqual(expected, actual);
        }
    }

    [TestFixture]
    public class ObjectExtensionCallMethodGetReturn
    {
        
        [Test]
        public void method_CallMethodGetReturn_given_FuncVoidInt_should_return_1()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That(widget.foo1() == (int)widget.CallMethodGetReturn("foo1"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncStringString_should_return_2()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That(widget.foo2("2") == (string)widget.CallMethodGetReturn("foo2(2)"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncStringString_should_return_3()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That(widget.foo3(3) == (int)widget.CallMethodGetReturn("foo3(3)"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncStringString_should_return_4Four()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That(widget.foo4(4, "Four") == (string)widget.CallMethodGetReturn("foo4(4,Four)"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncIntStringLongString_should_return_4Four4()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That( widget.foo5(5, "Five", 5) == (string)widget.CallMethodGetReturn("foo5(5,Five,5)"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncIntStringLongString_should_return_6Six6true()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            //-Assert
            Assert.That(widget.foo6(6, "Six", 6,true) == (string)widget.CallMethodGetReturn("foo6(6,Six,6,true)"));

        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncIntStringLongFloatString_should_return_7Seven7false1p1()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            var expected = widget.foo7(7, "Seven", 7, false, 1.1f);
            //-Assert
            Assert.AreEqual(expected , (string)widget.CallMethodGetReturn("foo7(7,Seven,7,false,1.1)"));

        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncIntStringLongFloatDoubleString_should_return_8Eight8true2p22p2()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            var expected = widget.foo8(8, "Eight", 8, true, 1.1f , 1.2d);
            //-Assert
            Assert.AreEqual(expected, (string)widget.CallMethodGetReturn("foo8(8,Eight,8,true,1.1,1.2)"));

        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncWidegtEnumString_should_return_expected_enum_value()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            var expected = widget.foo9(WidgetEnum.TwoEnum);
            //-Assert
            Assert.AreEqual(expected, (string)widget.CallMethodGetReturn("foo9(2)"));
        }

        [Test]
        public void method_CallMethodGetReturn_given_FuncWidegtEnumOtherString_should_return_expected_enum_value()
        {
            //-Arrange
            var widget = new Widget();
            //-Act
            var expected = widget.foo10(WidgetEnumOther.X2Enum);
            //-Assert
            Assert.AreEqual(expected, (string)widget.CallMethodGetReturn("foo10(2)"));
        }

        [Test]
        public void method_CallPropertyExclusiveMethodGetReturn_should_return_expected_value()
        {
            DateTime propValue = DateTime.Now;

            //-Arrange
            var widget = new Widget();
            widget.DateTimeProp = propValue;

            //-Act
            var expected = propValue.ToShortDateString();

            //-Assert
            Assert.AreEqual(expected, (string)widget.DateTimeProp.CallMethodGetReturn("ToShortDateString()"));
        }

        [Test]
        public void method_CallPropertyOverloadedMethodGetReturn_should_return_expected_value()
        {
            DateTime propValue = DateTime.Now;
            String dateFormat = "dd MMMM yyyy";
            //-Arrange
            var widget = new Widget();
            widget.DateTimeProp = propValue;

            //-Act
            var expected = propValue.ToString(dateFormat);

            //-Assert
            Assert.AreEqual(expected, (string)widget.DateTimeProp.CallMethodGetReturn("ToString(" + dateFormat + ")"));
        }

    }

}

